package ca.unbc.cpsc672.fuzzy;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import ca.unbc.cpsc672.fuzzy.function.Gaussian;
import ca.unbc.cpsc672.fuzzy.function.MembershipFunction;
import ca.unbc.cpsc672.fuzzy.function.Piecewise;
import ca.unbc.cpsc672.fuzzy.function.PiecewiseLast;
import ca.unbc.cpsc672.fuzzy.rule.IfFuzzyAnd;
import ca.unbc.cpsc672.fuzzy.rule.IfFuzzyOr;
import ca.unbc.cpsc672.fuzzy.rule.IfSingleSet;
import ca.unbc.cpsc672.fuzzy.rule.IfThenRule;

public class IOUtils {
	private static final String PIECEWISE_TYPE = "P";
	private static final String PIECEWISE_LAST_TYPE = "PL";
	private static final String GAUSSIAN_TYPE = "G";
	private static final String IF_THEN = "IF_THEN";
	private static final String IF_AND_THEN = "IF_AND_THEN";
	private static final String IF_OR_THEN = "IF_OR_THEN";

	public static final String FS_FN = "fuzzySet.csv";
	public static final String R_FN = "rules.csv";

	public static void saveFuzzySet(Collection<FuzzySet> fuzSet)
			throws IOException {
		try (BufferedWriter bw = new BufferedWriter(new FileWriter(FS_FN))) {
			for (FuzzySet set : fuzSet) {
				bw.append(set.toCSVString());
			}
		}
	}

	public static Map<String, FuzzySet> loadFuzzySet() throws IOException {
		return loadFuzzySet(FS_FN);
	}

	// popularity,2.0,Low,G,1.0,0.0,0.5,
	public static Map<String, FuzzySet> loadFuzzySet(String fn)
			throws IOException {
		HashMap<String, FuzzySet> map = new HashMap<>();
		try (BufferedReader br = new BufferedReader(new FileReader(fn))) {
			String line;
			while ((line = br.readLine()) != null) {
				String[] str = line.split(",");
				String setName = str[0];
				String label = str[2];
				String type = str[3];
				double v1 = Double.parseDouble(str[4]);
				double v2 = Double.parseDouble(str[5]);

				FuzzySet set;
				if (map.containsKey(setName))
					set = map.get(setName);
				else {
					double range = Double.parseDouble(str[1]);
					set = new FuzzySet(setName, range);
					map.put(setName, set);
				}
				MembershipFunction f;
				if (GAUSSIAN_TYPE.equals(type)) {
					f = new Gaussian(label, v1, v2);
				} else if (PIECEWISE_LAST_TYPE.equals(type)) {
					f = new PiecewiseLast(label, new double[] { v1, v2 });
				} else {
					double v3 = Double.parseDouble(str[6]);
					double v4 = Double.parseDouble(str[7]);
					f = new Piecewise(label, new double[] { v1, v2, v3, v4 });
				}
				set.addFunction(f);
			}
		}
		return map;
	}

	public static void saveRules(Collection<RuleSet> rules) throws IOException {
		try (BufferedWriter bw = new BufferedWriter(new FileWriter(R_FN))) {
			for (RuleSet rule : rules) {
				bw.append(rule.toCSVString());
			}
		}
	}
	
	public static Map<String, RuleSet> loadRuls()
			throws IOException {
		return loadRuls(R_FN, loadFuzzySet());
	}

	public static Map<String, RuleSet> loadRuls(Map<String, FuzzySet> fuzSetMap)
			throws IOException {
		return loadRuls(R_FN, fuzSetMap);
	}

	public static Map<String, RuleSet> loadRuls(String fn,
			Map<String, FuzzySet> fuzSetMap) throws IOException {
		HashMap<String, RuleSet> ret = new HashMap<>();
		try (BufferedReader br = new BufferedReader(new FileReader(fn))) {
			String line;
			while ((line = br.readLine()) != null) {
				String[] str = line.split(",");
				String name = str[0];
				RuleSet set;
				if (ret.containsKey(name))
					set = ret.get(name);
				else {
					set = new RuleSet(name);
					ret.put(name, set);
				}
				IfThenRule rule = loadOneRule(fuzSetMap, str);
				set.addRule(rule);
			}
		}
		return ret;
	}

	public static IfThenRule loadOneRule(Map<String, FuzzySet> fuzSetMap,
			String[] str) {
		String type = str[1];
		String v1 = str[2];
		String v2 = str[3];
		IfThenRule rule;
		if (IF_THEN.equals(type)) {
			MembershipFunction mf1 = findMF(v1, fuzSetMap);
			MembershipFunction mf2 = findMF(v2, fuzSetMap);
			rule = new IfThenRule(new IfSingleSet(mf1), mf2);
		} else {
			String v3 = str[4];
			MembershipFunction mf1 = findMF(v1, fuzSetMap);
			MembershipFunction mf2 = findMF(v2, fuzSetMap);
			MembershipFunction mf3 = findMF(v3, fuzSetMap);
			if (IF_AND_THEN.equals(type))
				rule = new IfThenRule(new IfFuzzyAnd(mf1, mf2), mf3);
			else
				rule = new IfThenRule(new IfFuzzyOr(mf1, mf2), mf3);
		}
		return rule;
	}

	private static MembershipFunction findMF(String fullName,
			Map<String, FuzzySet> fuzSetMap) {
		String[] name = fullName.split("\\.");
		FuzzySet fuzSet = fuzSetMap.get(name[0]);
		return fuzSet.getMembershipFunction(name[1]);
	}

	public static String getTypeString(Object obj) {
		if (obj instanceof Gaussian)
			return GAUSSIAN_TYPE;
		else if (obj instanceof Piecewise)
			return PIECEWISE_TYPE;
		else if (obj instanceof PiecewiseLast)
			return PIECEWISE_LAST_TYPE;
		else if (obj instanceof IfFuzzyAnd)
			return IF_AND_THEN;
		else if (obj instanceof IfFuzzyOr)
			return IF_OR_THEN;
		else if (obj instanceof IfSingleSet)
			return IF_THEN;
		else
			return null;
	}
}
